package makemachine.demos.audio.microphone.capture
{
	import com.bit101.components.Label;
	
	import flash.display.*;
	import flash.geom.*;
	import flash.utils.ByteArray;
	
	import makemachine.demos.audio.microphone.MicrophoneCapture;
	import makemachine.utils.*;
	
	/**
	 * Renders a visualization of the amplitude of an input signal
	 */
	public class InputRenderer extends Sprite
	{
		public function InputRenderer()
		{
			super();
			
			viewport = new Rectangle( 0, 0, 0, 0 );
			
			background = new Sprite();
			bar = new Sprite();
			fieldContainer = new Sprite();
			waveform = new Sprite();
			samples = new Vector.<Number>();
			
			resolution = 50;
			startTime = 0;
			endTime = 30000;
			setViewportSize( 610, 150 );
			renderBackground();
			
			addChild( background );
			addChild( waveform );
			addChild( bar );
			addChild( fieldContainer );
		}
		
		// --------------------------------------------------
		//
		// -- properties
		// 
		// --------------------------------------------------
		
		// -- view
		protected var viewport:Rectangle;
		protected var fieldContainer:Sprite;
		protected var background:Sprite;
		protected var waveform:Sprite;
		protected var xIncrement:Number;
		protected var bar:Sprite;
		
		// -- samples
		protected var samples:Vector.<Number>;
		protected var startSample:int;
		protected var endSample:int;
		
		// --------------------------------------------------
		//
		// -- get / setters
		// 
		// --------------------------------------------------
		
		// -- start point of render
		protected var _startTime:int;
		public function get startTime():int { return _startTime; }
		public function set startTime( value:int ):void 
		{
			_startTime = constrain( value, 0, _endTime );
			startSample = _startTime * ( MicrophoneCapture.SAMPLE_RATE / 1000 );
			updateXIncrement()
			renderBackground();
		}
		
		// -- end point of render
		protected var _endTime:int;
		public function get endTime():int { return _endTime; }
		public function set endTime( value:int ):void 
		{
			_endTime = constrain( value, _startTime, int.MAX_VALUE );
			endSample = _endTime * ( MicrophoneCapture.SAMPLE_RATE / 1000 );
			updateXIncrement();
			renderBackground();
		}
		
		// -- the number of samples between each render point
		protected var _resolution:int;
		public function get resolution():int { return _resolution; }
		public function set resolution( value:int ):void 
		{
			_resolution = constrain( value, 1, 1000 );
			updateXIncrement();
		}
		
		// -- sets width of renderer
		public function set viewportWidth( w:int ):void 
		{
			viewport.width = w;
			updateXIncrement();
			renderBackground();
		}
		
		// -- sets height of renderer
		public function set viewportHeight( h:int ):void 
		{
			viewport.height = h;
			updateXIncrement();
			renderBackground();
		}
		
		// - convenience method for updating width and height
		public function setViewportSize( w:int, h:int ):void 
		{
			viewport.width = w;
			viewport.height = h;
			updateXIncrement();
			renderBackground();
		}
		
		// --------------------------------------------------
		//
		// -- public methods
		// 
		// --------------------------------------------------
		
		// -- renders the input as a waveform
		// -- expects audio data
		public function render( data:ByteArray = null ):void
		{
			var i:int = 0;
			var l:int = samples.length;
			
			if( data )
			{
				data.position = 0;
				while( data.bytesAvailable )
				{
					samples[l+i] = data.readFloat();
					i++;
				}
			}
			
			var xpos:Number = 0;
			var g:Graphics = waveform.graphics;
			g.clear();
			g.lineStyle( 1, 0xf05151 );
			g.moveTo( 0, viewport.height * .5 + 10 );
			g.lineTo( viewport.width, viewport.height * .5 + 10 );
			g.moveTo( 0, viewport.height * .5 + 10 );
			
			if( samples.length > startSample )
			{
				var n:Number;
				for( i = startSample; i < endSample; i+= _resolution )
				{
					if( i < samples.length )
					{
						n = ( samples[i] * viewport.height + 5 ) + ( viewport.height * .5 + 5 );
						g.lineTo( xpos, n );
						xpos += xIncrement;
						continue
					}
					break;
				}
			}
		}
		
		// -- resets all parameters
		public function reset():void 
		{
			startTime = 0;
			endTime = 30000;
			waveform.graphics.clear();
			samples.splice( 0, samples.length );
			render();
		}
		
		// --------------------------------------------------
		//
		// -- protected methods
		// 
		// --------------------------------------------------
		
		// -- draws background, happens at initialization and when viewport is resized
		protected function renderBackground():void 
		{
			while( fieldContainer.numChildren > 0 ) fieldContainer.removeChildAt( 0 );
			
			var i:int;
			var xpos:Number
			var label:Label;
			var nearestStart:Number
			var nearestEnd:Number
			var total:Number
			var graticules:int
			var g:Graphics = background.graphics;
			
			g.clear();
			g.beginFill( 0x353535 );
			g.drawRect( 0, 0, viewport.width, viewport.height );
			g.endFill();
			
			// -- draw seconds
			g = bar.graphics;
			g.clear();
			g.beginFill( 0x444444 );
			g.drawRect( 0, 0, viewport.width, 20 );
			g.endFill();
			
			nearestStart = roundToInt( _startTime, 1000 );
			nearestEnd = roundToInt( _endTime, 1000 );
			total = nearestEnd - nearestStart;
			graticules = total / 1000;
			
			g.lineStyle( 1, 0x00c6ff );
			for( i = 0; i < graticules; i++ )
			{
				xpos = map( nearestStart + 1000 * i, _startTime, _endTime, 0, viewport.width );
				g.moveTo( xpos, 1 );
				g.lineTo( xpos, 15 );
				label = new Label( fieldContainer, xpos + 1, 5, String( ( nearestStart + 1000 * i ) / 1000 ) );
			}
			
			// -- draw half seconds
			nearestStart = roundToInt( _startTime, 500 );
			nearestEnd = roundToInt( _endTime, 500 );
			total = nearestEnd - nearestStart;
			graticules = total / 500;
			
			g.lineStyle( 1, 0x00c6ff, .5 );
			for( i = 0; i < graticules; i++ )
			{
				xpos = map( nearestStart + 500 * i, _startTime, _endTime, 0, viewport.width );
				g.moveTo( xpos, 1 );
				g.lineTo( xpos, 10 );
			}
			
			scrollRect = viewport;
		}
		
		protected function updateXIncrement():void 
		{
			xIncrement = viewport.width / ( ( endSample - startSample ) / _resolution );
		}
	}
}